import sys
import os
import json
import redis
import time
import shutil
from loguru import logger
import traceback
from datetime import datetime
from queue import Empty, Queue
from PySide6.QtCore import QRunnable, QThread, Signal, Slot, QObject, QTimer,QThreadPool
import requests
import copy
from gui.ui.UI_main_window import Ui_MainWindow
from gui.schemas.configs import configs

from gui.schemas.enumeration import AnnotationStatus, UserRole

# 命令类型枚举
class CommandType:
    EXIT = "exit"
    CONNECT = "connect"
    CREATE_JOB = "create_job"
    GET_JOBS = "get_jobs"
    SAVE_JOB = "save_job"
    GET_FILES_DATA = "get_files_data"
    SAVE_FILE_ANNOTATION = "save_file_annotation"
    GET_FILE_ANNOTATION = "get_file_annotation"
    ASSIGN_FILES = "assign_files"
    CLOSE_JOB = "close_job"
    ADD_MODEL = "add_model"
    DEL_MODEL = "del_model"
    GET_ALL_MODELS = "get_all_models"
    GET_MODEL = "get_model"

# 命令响应状态
class ResponseStatus:
    SUCCESS = "success"
    FAILURE = "failure"
    ERROR = "error"

# 数据管理器命令类 - 封装线程间传递的命令
class DataCommand:
    def __init__(self, cmd_type, params=None, callback=None):
        self.type = cmd_type
        self.params = params or {}
        self.callback = callback  # 回调信号

def _parse_time(time_str):
    """解析时间字符串为datetime对象"""
    if not time_str:
        return None
    try:
        if "+" in time_str or "Z" in time_str:
            return datetime.fromisoformat(time_str.replace("Z", "+00:00"))
        else:
            return datetime.fromisoformat(time_str)
    except:
        return None

# 数据管理器信号类 - 分离信号定义，避免跨线程问题
class DataManagerSignals(QObject):
    # 连接状态信号
    connected = Signal(bool, str)  # (状态, 消息)
    # 命令执行结果信号
    command_result = Signal(str, dict, str)  # (命令类型, 结果数据, 状态)
    # 错误信号
    error_occurred = Signal(str)
    task_finished = Signal()
    # 获取标注数据信号
    annotation_data = Signal(int, dict)  # (任务ID, 标注数据)



# 数据管理器 - 处理与Redis和本地文件的交互（线程安全版本）
class DataManager(QRunnable):
    """
    线程安全的数据管理器，在独立线程中运行，通过信号槽与主线程通信
    """
    
    def __init__(self, main_panel:Ui_MainWindow=None,user_id=-1, username="", role="", redis_host='localhost', redis_port=6379, 
                 redis_password="123456", redis_db=0):
        super().__init__()
        self.main_panel = main_panel
        self.user_id = user_id
        self.username = username
        # self.main_panel.userinfo["roles"] = role
        self.host = redis_host
        self.port = redis_port
        self.password = redis_password
        self.db = redis_db
        self.redis_client = None
        self.file_datas = {}
        self.connected_status = False
        self.command_queue = Queue()  # 命令队列
        self.local_data_dir = os.path.join("./local_data", username)
        self.models_key = "models"
        self.reconnect_timer = QTimer()
        self.reconnect_timer.setInterval(5000)  # 5秒重连间隔
        self.reconnect_timer.timeout.connect(self._try_reconnect)
        self.is_thread_terminated = False  # 新增线程终止标志
        # 初始化信号对象
        self.signals = DataManagerSignals()
        self.set_userinfo(username,role)
        
    def set_userinfo(self,user_id="",username="",role="",**kwargs):
        if user_id:
            self.user_id = user_id
            self.username = username
            # self.local_data_dir = os.path.join("./local_data", username)
            # os.makedirs(self.local_data_dir, exist_ok=True)
        # self.main_panel.userinfo["roles"] = role
        
    def run(self):
        """QRunnable的run方法，在工作线程中执行"""
        try:
            self._initialize()
            self._process_commands()
        except Exception as e:
            self.signals.error_occurred.emit(f"数据管理器运行异常: {str(e)}")
            traceback.print_exc()
        finally:
            self._cleanup()
            pass
            # self.signals.task_finished.emit()
    
    def stop(self):
        """停止数据管理器线程"""
        try:
            self.is_terminated = True
            self.enqueue_command(CommandType.EXIT)  # 放入None作为终止信号
            self.reconnect_timer.stop()
            # 等待任务处理完成（可选）
            # QThreadPool.globalInstance().waitForDone(5000)  # 等待5秒
        except Exception as e:
            self.signals.error_occurred.emit(f"线程退出异常: {str(e)}")
            traceback.print_exc()

    def _cleanup(self):
        """线程结束时的资源清理（优化版）"""
        print(f"开始清理数据管理器线程 (PID: {os.getpid()})")
        
        # 2. 清空命令队列（防止队列残留对象）
        while not self.command_queue.empty():
            try:
                self.command_queue.get_nowait()
                self.command_queue.task_done()
            except Empty:
                break
        
        # 3. 关闭Redis连接（增加异常处理）
        if self.redis_client:
            try:
                self.redis_client.close()
                print("Redis连接已安全关闭")
            except Exception as e:
                self.signals.error_occurred.emit(f"关闭Redis连接失败: {str(e)}")
                print(f"Redis连接关闭异常: {str(e)}")
            finally:
                self.redis_client = None
        
        # # 4. 停止重连定时器
        # if self.reconnect_timer and self.reconnect_timer.isActive():
        #     self.reconnect_timer.stop()
        #     print("重连定时器已停止")
        
        # 5. 重置连接状态
        self.connected_status = False
        print("数据管理器线程资源清理完成")
        
    def _initialize(self):
        """线程初始化"""
        try:
            self.connected_status = self.connect()
            # asyncio.run(self._async_connect())
            if self.connected_status:
                self.signals.connected.emit(True, "连接成功")
            else:
                self.signals.connected.emit(False, "连接失败，尝试重连...")
                self.reconnect_timer.start()
        except Exception as e:
            self.signals.error_occurred.emit(f"初始化错误: {str(e)}")
            self.signals.connected.emit(False, str(e))
            self.reconnect_timer.start()
    
    def _try_reconnect(self):
        """尝试重连到Redis"""
        if not self.connected_status:
            self.connected_status = self.connect()
            # asyncio.run(self._async_connect())
            if self.connected_status:
                self.signals.connected.emit(True, "重连成功")
                self.reconnect_timer.stop()
            else:
                self.signals.connected.emit(False, "重连失败，5秒后重试...")
    
    def _process_commands(self):
        """处理命令队列"""
        while True:
            if self.is_thread_terminated:
                break
            try:
                if not self.command_queue.empty():
                    command = self.command_queue.get(block=False)
                    if command.type == CommandType.EXIT:
                        break
                    self._execute_command(command)
                    self.command_queue.task_done()
                else:
                    time.sleep(0.01)  # 避免CPU占用过高
            except redis.exceptions.TimeoutError as e:
                self.connected_status = False
                self.signals.error_occurred.emit(f"命令处理错误: {str(e)}")
            except RuntimeError as e:
                logger.exception(e)
                break
            except Exception as e:
                logger.exception(f"命令处理错误: {str(e)}")
                self.signals.error_occurred.emit(f"命令处理错误: {str(e)}")
                traceback.print_exc()
    
    def _execute_command(self, command):
        """执行具体命令"""
        result = {}
        status = ResponseStatus.ERROR
        
        try:
            if command.type == CommandType.CONNECT:
                result = {"connected": self.connect()}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.CREATE_JOB:
                result = {"job_name": self.create_job(command.params.get("job_data"), 
                                                    command.params.get("select_files"))}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.GET_JOBS:
                result = {"jobs": self.get_jobs()}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.SAVE_JOB:
                self.save_job(command.params.get("job_info"))
                result = {"success": True}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.GET_FILES_DATA:
                result = {"file_datas": self.get_files_data(command.params.get("job_data"))}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.SAVE_FILE_ANNOTATION:
                self.save_file_annotation(
                    command.params.get("job_id"),
                    command.params.get("file_annotation"),
                    command.params.get("reviews")
                )
                result = {"success": True}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.GET_FILE_ANNOTATION:
                result = {"file_annotation": self.get_file_annotation(
                    command.params.get("job_id"),
                    command.params.get("filename")
                )}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.ASSIGN_FILES:
                self.assign_files(command.params.get("job_name"), command.params.get("user_files"))
                result = {"success": True}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.CLOSE_JOB:
                self.close_job(command.params.get("job_id"))
                result = {"success": True}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.ADD_MODEL:
                self.add_model(
                    command.params.get("model_name"),
                    command.params.get("labels"),
                    command.params.get("det_type")
                )
                result = {"success": True}
                status = ResponseStatus.SUCCESS
            elif command.type == CommandType.DEL_MODEL:
                self.del_model(
                    command.params.get("model_name"),
                )
                result = {"success": True}
                status = ResponseStatus.SUCCESS
            elif command.type == CommandType.GET_ALL_MODELS:
                result = {"models": self.get_all_models()}
                status = ResponseStatus.SUCCESS
                
            elif command.type == CommandType.GET_MODEL:
                result = {"model": self.get_model(command.params.get("model_name"))}
                status = ResponseStatus.SUCCESS
                
        except redis.exceptions.TimeoutError as e:
            self.connected_status = False
            self.signals.error_occurred.emit(f"命令处理错误: {str(e)}")
        except Exception as e:
            status = ResponseStatus.FAILURE
            result = {"error": str(e)}
            self.signals.error_occurred.emit(f"命令执行错误: {str(e)}")
            traceback.print_exc()
        
        # 发送命令执行结果
        self.signals.command_result.emit(command.type, result, status)
        # 调用回调信号
        if command.callback and hasattr(self, command.callback):
            getattr(self, command.callback).emit(result, status)
    
    def enqueue_command(self, cmd_type, params=None, callback=None):
        """向命令队列添加命令"""
        command = DataCommand(cmd_type, params, callback)
        self.command_queue.put(command)
    
    def connect(self):
        """连接到Redis服务器（线程安全版本）"""
        try:
            if self.redis_client and self.redis_client.ping():
                return True
                
            self.redis_client = redis.Redis(
                host=self.host,
                port=self.port,
                password=self.password,
                db=self.db,
                decode_responses=True,
                socket_timeout=1,
            )
            
            if self.redis_client.ping():
                self.connected_status = True
                return True
            else:
                self.connected_status = False
                return False
        except Exception as e:
            self.connected_status = False
            self.signals.error_occurred.emit(f"连接Redis出错: {str(e)}")
            return False
    
    def create_job(self, job_data, select_files):
        """创建新工作（线程安全版本）"""
        if UserRole.ADMIN not in self.main_panel.userinfo["roles"] and not self.connected_status:
            raise PermissionError("只有管理员才能创建工作或未连接到Redis")
        
        name = job_data["name"]
        model_name = job_data["model_name"]
        model_data = self.get_model(model_name)
        
        if model_data:
            job_data["labels"] = model_data["labels"]
            job_data["det_type"] = model_data["det_type"]
        
        job_data["status"] = "open"
        if "user_metadata" not in job_data:
            job_data["user_metadata"] = {
                "config": {},
                "job": {"current_id": 0}
            }
        
        self.save_job(job_data)
        
        base_url = job_data["base_url"]
        file_type = job_data["file_type"]
        
        for filename in select_files:
            file_url = os.path.join(base_url, filename)
            if base_url.startswith("http"):
                file_url = f"{base_url}/{filename}"
                size = 0
            else:
                size = os.path.getsize(file_url)
            
            file_data = {
                "filename": filename,
                "size": size,
                "file_attributes": {
                    "file_url": file_url,
                    "worker": "",
                    "transliterate": "",
                    "class_name": "",
                },
                "annotation": [],
                "owner": "",
                "status": "未分配",
            }
            
            reviews = {
                "filename": filename,
                "comment": "",
                "regions": [],
            }
            
            self.save_file_annotation(name, file_data, reviews)
        
        return name
    
    def check_updatetime(self, redis_updatetime, locals_updatetime):
        return redis_updatetime > locals_updatetime
    
    def get_jobs(self):
        """获取所有工作,确保从最新源中获取数据即可"""
        jobs = {}
        close_jobs = []
        if self.connected_status: # 从redis获取
            job_keys = self.redis_client.keys("task:*")
            for job_key in job_keys:
                if not job_key.endswith(":base"): continue
                redis_job_info = self.redis_client.hgetall(job_key)
                # 读取 user_metadata 数据
                task_id = redis_job_info["id"]
                name = redis_job_info["name"]
                status = int(redis_job_info["status"])
                if status != 1: # 任务状态属于非打开状态跳过
                    close_jobs.append(name)
                    continue
                user_metadata = self.redis_client.hgetall(f"task:{task_id}:user_metadata:{self.user_id}")
                if user_metadata:
                    if "job" in user_metadata:
                        user_metadata["job"] =json.loads(user_metadata["job"])
                    if "config" in user_metadata:
                        user_metadata["config"] =json.loads(user_metadata["config"])
                    redis_job_info["user_metadata"] = user_metadata
                else:
                    redis_job_info["user_metadata"] = { # 用户自定义数据，不同用户不能修改
                        "config": {}, # 其他配置项，如快捷键等
                        "job": {
                            "current_id": 0 # 当前用户标注文件列表所在的索引
                        }
                    }
                
                # 将 JSON 字符串转换为列表
                if "attributes" in redis_job_info:
                    redis_job_info["attributes"] = json.loads(redis_job_info["attributes"])
                redis_job_info["updatetime"] = _parse_time(redis_job_info.get("updatetime", datetime.now().isoformat()))
                jobs[task_id] = redis_job_info
        # # 
        # if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]: # 如果是 标注员，要从本地或redis中加载最新的数据
        #     job_keys = list(set(os.listdir(self.local_data_dir) + list(jobs.keys())))
        #     for job_key in job_keys:
        #         if job_key in close_jobs: # 对于关闭的任务, 确保全部数据上传后删除
        #             continue
        #         # if job_key not in jobs: # 如果不存在，则从ti
        #         #     continue
        #         job_dir = os.path.join(self.local_data_dir, job_key)
        #         os.makedirs(job_dir, exist_ok=True)
        #         locals_job_info_file = os.path.join(self.local_data_dir, job_key, ".info")
        #         if os.path.exists(locals_job_info_file):
        #             with open(locals_job_info_file, "r",encoding="utf-8") as f:
        #                 locals_job_info = json.load(f)
        #                 locals_job_info["updatetime"] = _parse_time(locals_job_info["updatetime"])
                        
        #                 if job_key in jobs: # 如果都存在就比较最新的
        #                     redis_job_info = jobs[job_key]
        #                     if self.check_updatetime(redis_job_info["updatetime"], locals_job_info["updatetime"]): # 如果redis数据比本地数据更新，则使用redis数据
        #                         self.save_job(redis_job_info)
        #                     else: # 如果本地数据新，则使用本地数据
        #                         jobs[job_key]["user_metadata"] = locals_job_info["user_metadata"] # 非管理员只能更新user_metadata数据
        #                         self.save_job(jobs[job_key])
        #                 else: # 如果redis数据不存在，则使用本地数据
        #                     jobs[job_key] = locals_job_info
        #         elif job_key in jobs: # 如果本地数据不存在，则使用redis数据,并保存到本地
        #             redis_job_info = jobs[job_key]
        #             self.save_job(redis_job_info)
        return jobs
    
    def save_job(self,job_infos:dict):
        """保存job信息"""
        job_info = copy.deepcopy(job_infos)
        task_id = job_info["id"]
        name = job_info["name"]
        # job_info["createtime"] = datetime.now().isoformat()
        job_info["updatetime"] = datetime.now().isoformat()
        # if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]: # 如果是 标注员 保存到本地
        #     locals_job_info_file = os.path.join(self.local_data_dir, task_id, ".info")
        #     os.makedirs(os.path.dirname(locals_job_info_file),exist_ok=True)
        #     with open(locals_job_info_file, "w",encoding="utf-8") as f:
        #         job_info_str = json.dumps(job_info, ensure_ascii=False, indent=2)
        #         f.write(job_info_str)
                
        if self.connected_status: # 保存到redis
            user_metadata = job_info.pop("user_metadata")
            user_metadata["config"] = json.dumps(user_metadata.get("config",{}),ensure_ascii=False)
            user_metadata["job"] = json.dumps(user_metadata.get("job",{}),ensure_ascii=False)
            user_metadata["updatetime"] = datetime.now().isoformat()

            self.redis_client.hset(f"task:{task_id}:user_metadata:{self.user_id}", mapping=user_metadata)
            
            # 将列表类型的字段转换为 JSON 字符串
            if "attributes" in job_info and isinstance(job_info["attributes"], dict):
                job_info["attributes"] = json.dumps(job_info["attributes"])
            self.redis_client.hset(f"task:{task_id}:base", mapping=job_info)

    def get_key(self, match):
        # 初始游标为 0
        cursor = 0
        all_keys = []

        while True:
            # 调用 scan 方法
            cursor, keys =  self.redis_client.scan(cursor=cursor, match=match, count=100)  # 每次尝试返回 100 个键
            all_keys.extend(keys)
            # 游标为 0 时结束迭代
            if cursor == 0:
                break
        return all_keys

    def get_files_data(self, job_data):
        file_datas = {}
        job_name = job_data.get("name")
        job_id = job_data.get("id")
        anno_ids = []
        if self.connected_status: # 从redis获取
            if (self.main_panel.radioButton_check_mode.isChecked() or # 检查模式
                (self.main_panel.radioButton_correct_mode.isChecked()  # 修正员的校正模式
                 and UserRole.EMEND in self.main_panel.userinfo["roles"] )): # 可以获取所有标注数据
                anno_id_keys = self.get_key(f"task_info:{job_id}:anno:*")
                for anno_id_key in anno_id_keys:
                    anno_ids.append(anno_id_key.split(":")[-1])
            else: # 其他的情况回去自己的标注数据
                # anno_id_keys = self.redis_client.keys(f"task_info:{job_id}:assigned_annos:{self.user_id}:*")
                anno_id_keys = self.redis_client.smembers(f"task_info:{job_id}:assigned_annos:{self.user_id}")
                for anno_id_key in anno_id_keys:
                    anno_ids.append(anno_id_key.split(":")[-1])

            
        # if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]: # 如果是 标注员，要从本地或redis中加载最新的数据
        #     job_dir = os.path.join(self.local_data_dir, job_id)
        #     job_filenames = os.listdir(job_dir)
        #     for filename in job_filenames:
        #         anno_id, ext = os.path.splitext(filename)
        #         if anno_id not in anno_ids and ext in [".json"]:
        #             anno_ids.append(anno_id)
        if len(anno_ids) == 0: # 没有标注数据，分配文件
            new_assign_anno_ids = self.assign_files(job_id)
            anno_ids.extend(new_assign_anno_ids)
        for anno_id in anno_ids:
            file_annotation = self.get_file_annotation(job_id,anno_id)
            if file_annotation:
                file_datas[file_annotation["filename"]] = file_annotation
                # self.signals.annotation_data.emit(job_id, file_annotation)
        self.file_datas[job_id] = file_datas
        return file_datas
            
    def save_file_annotation(self, job_id, file_annotation = None,reviews = None):
        if file_annotation and UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]:
            file_annotation["updatetime"] = datetime.now().isoformat()
            anno_id = file_annotation["id"]
                
            # job_dir = os.path.join(self.local_data_dir, job_id)
            # os.makedirs(job_dir, exist_ok=True)
            # basename = os.path.splitext(filename)[0]
            # file_data_path = os.path.join(job_dir, anno_id + ".json")
            # if "annotation" in file_annotation and isinstance(file_annotation["annotation"],str):
            #     file_annotation["annotation"] = json.loads(file_annotation["annotation"])
            # with open(file_data_path, "w", encoding="utf-8") as f:
            #     json.dump(file_annotation, f, ensure_ascii=False, indent=2)
            # 
            if self.connected_status: # 保存到redis
                if file_annotation:
                    if "annotation" in file_annotation and not isinstance(file_annotation["annotation"],str):
                        file_annotation["annotation"] = json.dumps(file_annotation["annotation"],ensure_ascii=False)
                    if "reviews" in file_annotation:
                        file_annotation.pop("reviews")
                    self.redis_client.hset(f"task_info:{job_id}:anno:{anno_id}", mapping=file_annotation)
                    self.file_datas[job_id][file_annotation["filename"]] = file_annotation
                    unassigned_count = len([ann for ann in self.file_datas[job_id].values() if ann["status"] in [AnnotationStatus.UNASSIGNED,AnnotationStatus.ASSIGNED,]])

                    if unassigned_count < 10:
                        anno_ids = self.assign_files(job_id,max(1,10-unassigned_count))
                        for anno_id in anno_ids:
                            file_annotation = self.get_file_annotation(job_id,anno_id)
                            if file_annotation:
                                self.file_datas[job_id][file_annotation["filename"]] = file_annotation
                                self.signals.annotation_data.emit(int(job_id), file_annotation)

                    
        if reviews:
            reviews["updatetime"] = datetime.now().isoformat()
            anno_id = reviews["id"]
            if UserRole.EMEND in self.main_panel.userinfo["roles"]: # 检测员，只能修改评论
                if self.connected_status: # 保存到redis
                    reviews["annotation"] = json.dumps(reviews["annotation"],ensure_ascii=False)
                    self.redis_client.hset(f"task_info:{job_id}:reviews:{anno_id}", mapping=reviews)
                    
        
    def get_file_annotation(self, job_id, anno_id):
        """获取文件的标注数据"""
        # job_id = job_data.get("id")
        file_data = None
        redis_file_data = None
        redis_file_reviews = None
        if self.connected_status: # 从redis获取
            redis_file_data = self.redis_client.hgetall(f"task_info:{job_id}:anno:{anno_id}")
            redis_file_reviews = self.redis_client.hgetall(f"task_info:{job_id}:reviews:{anno_id}")
            if redis_file_data:
                file_info_id = redis_file_data.pop("file_info_id")
                file_info = self.redis_client.hgetall(f"task_info:{job_id}:file:{file_info_id}")
                redis_file_data["path"] = file_info.pop("path")
                redis_file_data["filename"] = file_info.pop("name")
                if "annotation" in redis_file_data:
                    redis_file_data["annotation"] = json.loads(redis_file_data["annotation"])
                # if "regions" in redis_file_data:
                #     redis_file_data["regions"] = json.loads(redis_file_data["regions"])
                if not redis_file_reviews: # 默认评论为空
                    redis_file_reviews = {
                        "id" : anno_id,
                        "comment": "", # 
                        "annotation": [], 
                    }
                else:
                    redis_file_reviews["annotation"] = json.loads(redis_file_reviews["annotation"])
                file_data = redis_file_data
                file_data["reviews"] = redis_file_reviews
                if (self.main_panel.radioButton_check_mode.isChecked() or # 检查模式
                    (self.main_panel.radioButton_correct_mode.isChecked()  # 修正员的校正模式
                    and UserRole.EMEND in self.main_panel.userinfo["roles"] )): # 可以获取所有标注数据
                    pass
                else: # 标注员，只能获取自己的数据
                    if redis_file_data["worker_id"] == "" or redis_file_data["worker_id"] != str(self.user_id): # 不是自己的数据，跳过
                        return 
    
        # if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]: # 如果是 标注员，要从本地或redis中加载最新的数据
        #     job_dir = os.path.join(self.local_data_dir, job_id)
        #     # basename = os.path.splitext(filename)[0]
        #     locals_annon_file_path = os.path.join(job_dir, f"{anno_id}.json")
        #     # 读取本地文件，对比redis数据
        #     if os.path.exists(locals_annon_file_path):
        #         with open(locals_annon_file_path, "r", encoding="utf-8") as f: #判断标注文件
        #             locals_annon_data = json.load(f)
        #             if redis_file_data: # 如果都存在就比较最新的
        #                 file_data = redis_file_data
        #                 if not self.check_updatetime(redis_file_data["updatetime"], locals_annon_data["updatetime"]): # 如果redis数据比本地数据更新，则使用redis数据
        #                     file_data = locals_annon_data
        #                     self.save_file_annotation(job_id,file_data) # redis 数据最新保存文件数据
        #             else: # 如果redis数据不存在，则使用本地数据
        #                 file_data = locals_annon_data
        #                 self.save_file_annotation(job_id,file_data) # 保存文件数据到redis
        #     elif redis_file_data: # 如果redis数据存在，本地数据不存在，则使用redis数据保存
        #         self.save_file_annotation(job_id,redis_file_data) # 保存文件数据
            
        #     # 获取下载文件
        #     filename = file_data["filename"]
        #     locals_file_path = os.path.join(job_dir, filename)
        #     if not os.path.exists(locals_file_path):
        #         try:
        #             file_url = configs.IMAGES_DOWNLOAD_BASE_URL + file_data["path"] # 获取文件下载地址
        #             if file_url.startswith("http"): # 可以是https
        #                 # file_url = f"{file_path}/{filename}"
        #                 file_url = configs.IMAGES_DOWNLOAD_BASE_URL + "?file_path=" + file_data["path"] # 获取文件下载地址
        #                 r = requests.get(file_url)
        #                 with open(locals_file_path, "wb") as f:
        #                     f.write(r.content)
        #             else:
        #                 # file_url = os.path.join(base_url, filename)
        #                 shutil.copy(file_url, locals_file_path)
        #             file_data["locals_file_path"] = locals_file_path
        #         except Exception as e:
        #             print(f"获取 {file_url} 文件失败: {e}")
        #             traceback.print_exception(e)
        #             return 
                
                
        if "annotation" in file_data and isinstance(file_data["annotation"],str):
            file_data["annotation"] = json.loads(file_data["annotation"])
        return file_data
    
        
    def assign_files(self, task_id, count = 10):
        """分配文件给用户"""
        ann_ids = self.redis_client.spop(f"task_info:{task_id}:unassigned_annos", count) # 从未分配队列中弹出count个文件
        assign_ann_ids = []
        if ann_ids:
            for ann_id in ann_ids:
                assign_ann_ids.append(ann_id)
                self.redis_client.sadd(f"task_info:{task_id}:assigned_annos:{self.user_id}", ann_id) # 加入已分配队列
                self.redis_client.hset(f"task_info:{task_id}:anno:{ann_id}", "worker_id", str(self.user_id)) # 加入已分配队列
                self.redis_client.hset(f"task_info:{task_id}:anno:{ann_id}", "status", AnnotationStatus.ASSIGNED) # 加入已分配队列
                self.redis_client.hset(f"task_info:{task_id}:anno:{ann_id}", "updatetime", datetime.now().isoformat()) # 加入已分配队列
        return assign_ann_ids

        
    
    def add_model(self, model_name, labels, det_type):
        """添加模型"""
        model_data = {
            "labels": labels,
            "det_type": det_type
        }
        self.redis_client.hset(self.models_key, model_name, json.dumps(model_data))
    def del_model(self, model_name):
        """删除模型"""
        self.redis_client.hdel(self.models_key, model_name)
    
    def get_all_models(self):
        """获取所有模型"""
        models = self.redis_client.hgetall(self.models_key)
        for key in list(models.keys()):
            models[key] = json.loads(models[key])
        return models
    
    def get_model(self, model_name):
        """获取单个模型信息"""
        model_data = self.redis_client.hget(self.models_key, model_name)
        if model_data:
            return json.loads(model_data)
        return None
